home *** CD-ROM | disk | FTP | other *** search
/ L' Effet Pommier 3 / L'Effet Pommier - Volume 03.iso / Programmation / gray image 2.1 / read_tiff.cc < prev    next >
Text File  |  1995-10-08  |  11KB  |  391 lines

  1. // This may look like C code, but it is really -*- C++ -*-
  2. /*
  3.  ************************************************************************
  4.  *
  5.  *               Grayscale Images
  6.  *
  7.  *         Read an image from a file in the TIFF format
  8.  *
  9.  * The present TIFF reader reads a Class G TIFF file (for gray-scale
  10.  * images), See Appendix G of the TIFF specification. 
  11.  *
  12.  * The program really needs the following tags
  13.  *    ImageWidth
  14.  *    ImageLength
  15.  *    RowsPerStrip
  16.  *    StripOffsets
  17.  * and can't work without them. Yet, if RowsPerStrip is left out, we
  18.  * assume there is only one strip in the image
  19.  *
  20.  * In addition, class G TIFF files are required to have the following
  21.  * tags with the following values
  22.  *    SamplesPerPixel = 1
  23.  *    BitsPerSample   = 4 or 8
  24.  *    Compression    = 1 or 5(LZW)
  25.  *    PhotometricInterpretation = 0 or 1
  26.  * So, if these tags are specified, the program makes sure that their values
  27.  * are those that are supposed to be for class G TIFFs.
  28.  *
  29.  * At present, no compression is supported.
  30.  *
  31.  * $Id: read_tiff.cc,v 2.0 1995/03/06 21:58:09 oleg Exp oleg $
  32.  *
  33.  ************************************************************************
  34.  */
  35.  
  36. #include "image.h"
  37. #include "endian_io.h"
  38. #include "tiff.h"
  39. #include <iostream.h>
  40.  
  41.                 // Read a TIFF header from the file
  42.                 // and check it
  43. TIFFHeader::TIFFHeader(EndianIn& file)
  44. {
  45.   magic = file.read_short("Reading a TIFF file magic byte");
  46.   if( magic == TIFF_BIGENDIAN )
  47.     file.set_bigendian();
  48.   else if( magic == TIFF_LITTLEENDIAN )
  49.     file.set_littlendian();
  50.   else
  51.     _error("Unknown magic word %x",magic);
  52.   version = file.read_short("Reading TIFF file version");
  53.   diroffset = file.read_long("Reading the TIFF dir offset");
  54. }
  55.  
  56. /*
  57.  *------------------------------------------------------------------------
  58.  *            TIFF Directory
  59.  */
  60.  
  61. class TIFFDirectory;
  62.  
  63. class TIFFDirReadItem : public TIFFDirEntry
  64. {
  65.   friend class TIFFDirectory;
  66.                 // Note, this is a private constructor
  67.                 // Be sure to call the load(file) function
  68.                 // to finish the initialization
  69.   TIFFDirReadItem(void) { tag = 0; }
  70.   void load(EndianIn& file);
  71.  
  72. public:
  73.   void print(void) const;
  74. };
  75.  
  76.                 // Load up the item from the file: finish
  77.                 // the construction of the object
  78. void TIFFDirReadItem::load(EndianIn& file)
  79. {
  80.   tag   = file.read_short("Reading dir entry");
  81.   type  = file.read_short("Reading dir entry");
  82.   count = file.read_long("Reading dir entry");
  83.   val_offset = file.read_long("Reading dir entry");
  84. }
  85.  
  86.                 // Print the contents of an item w/o frills
  87. void TIFFDirReadItem::print(void) const
  88. {
  89.   message("tag %d, %d ",tag,count);
  90.   switch(type)
  91.   {
  92.     case BYTE:
  93.          message("bytes");
  94.      break;
  95.  
  96.     case ASCII:
  97.          message("characters");
  98.      break;
  99.  
  100.     case SHORT:
  101.          message("short ints");
  102.      break;
  103.  
  104.     case LONG:
  105.          message("long ints");
  106.      break;
  107.  
  108.     case RATIONAL:
  109.          message("rational numbers");
  110.      break;
  111.  
  112.     default:
  113.      _error("Invalid type %d",type);
  114.   }
  115.   message(" value %d (0x%x)\n",val_offset,val_offset);
  116. }
  117.  
  118.  
  119. class TIFFDirectory
  120. {
  121.   int no_entries;
  122.   TIFFDirReadItem * entries;
  123.  
  124. public:
  125.   TIFFDirectory(EndianIn& file);
  126.   ~TIFFDirectory(void);
  127.   void print(void) const;
  128.                 // Look up a scalar value in the directory
  129.   unsigned long int look_up(const unsigned short tag,
  130.                 const unsigned long default_value) const;
  131.                 // Look up a string info and read the string
  132.   char * read_str(const unsigned short tag, EndianIn& file) const;
  133.  
  134.                 // Look up an item in the directory by its
  135.                 // tag. Return 0 if not found
  136.   TIFFDirReadItem * look_up(const unsigned short tag) const;
  137. };
  138.  
  139.                 // Load up the directory from the file
  140. TIFFDirectory::TIFFDirectory(EndianIn& file)
  141. {
  142.   TIFFHeader header(file);
  143.   assert( file.seekg(header.directory_offset()).good() );
  144.   no_entries = file.read_short("no of entries");
  145.   entries = new TIFFDirReadItem[no_entries];
  146.  
  147.   register int i;
  148.   for(i=0; i<no_entries; i++)
  149.     entries[i].load(file);
  150.  
  151.   const int offset_to_next_dir = file.read_long("reading dir");
  152.   if( offset_to_next_dir != 0 )
  153.     message("The TIFF file contains several images, only the first one "
  154.         "is going to be considered");
  155. }
  156.  
  157.                 // Destroy all entries in the directory
  158. TIFFDirectory::~TIFFDirectory(void)
  159. {
  160.   assert( entries != 0 );
  161.   delete [] entries;
  162. }
  163.  
  164.                 // Print the contents of the directory
  165. void TIFFDirectory::print(void) const
  166. {
  167.   register int i;
  168.   message("\nThere are %d entries in the TIFF file directory\nThey are\n",
  169.       no_entries);
  170.   for(i=0;i<no_entries; i++)
  171.     entries[i].print();
  172. }
  173.  
  174.                 // Look up an item in the directory by its
  175.                 // tag. Return 0 if not found
  176.                 // Note, we take advantage of the fact
  177.                 // that all items in the directory are
  178.                 // in the ascending order of their tags
  179. TIFFDirReadItem * TIFFDirectory::look_up(const unsigned short tag) const
  180. {
  181.   int lo = 0;                // Using a binary search
  182.   int hi = no_entries;
  183.   while( hi > lo )
  184.   {
  185.     int middle = (hi+lo)/2;
  186.     if( entries[middle].tag == tag )
  187.       return &entries[middle];
  188.     else if( entries[middle].tag > tag )
  189.       hi = middle;
  190.     else
  191.       lo = middle+1;
  192.   }
  193.   return 0;                // Means not found
  194. }
  195.  
  196.  
  197.                 // Look up a scalar value in the directory
  198. unsigned long int TIFFDirectory::look_up(const unsigned short tag,
  199.                 const unsigned long default_value) const
  200. {
  201.   TIFFDirEntry * entryp = look_up(tag);
  202.   if( entryp == 0 )
  203.     return default_value;
  204.  
  205.   assert( entryp->tag == tag);
  206.   assert( entryp->count == 1 );        // Check that the value is scalar
  207.                     // Note, if the value is shorter
  208.                     // than long, it's LEFT justified
  209.   switch( entryp->type )
  210.   {
  211.     case TIFFDirEntry::BYTE:
  212.     case TIFFDirEntry::ASCII:
  213.          return (entryp->val_offset) >> 24;
  214.  
  215.     case TIFFDirEntry::SHORT:
  216.          return (entryp->val_offset) >> 16;
  217.  
  218.     case TIFFDirEntry::LONG:
  219.     case TIFFDirEntry::RATIONAL:
  220.          return (entryp->val_offset);
  221.  
  222.     default:
  223.      _error("Invalid type %d",entryp->type);
  224.   }
  225. }
  226.  
  227.                 // Look up a string value in the directory
  228.                 // and return a dynamically allocated str
  229.                 // Return 0 if not found
  230. char * TIFFDirectory::read_str(const unsigned short tag, EndianIn& file) const
  231. {
  232.   TIFFDirEntry * entryp = look_up(tag);
  233.   if( entryp == 0 )
  234.     return 0;
  235.   assert( entryp->tag == tag);
  236.   assert( entryp->type == TIFFDirEntry::ASCII );
  237.   
  238.   int size = entryp->count;
  239.   if( size <= 0 )
  240.     return 0;
  241.  
  242.   char * str = new char[size];;
  243.  
  244.   assert( file.seekg(entryp->val_offset).good() );
  245.  
  246.   file.read(str,size);
  247.   if( str[size-1] != '\0' )        // Make sure the str is terminated
  248.     str[size-1] = '\0';            // properly     
  249.  
  250.   return str;
  251. }
  252.  
  253.  
  254. class TIFFReadStrips
  255. {
  256.   int no_strips;
  257.   int rows_per_strip;
  258.   unsigned long int * strip_offsets;
  259. public:
  260.   TIFFReadStrips(const TIFFDirectory& directory,const int _rows_per_strip,
  261.          EndianIn& file);
  262.   TIFFReadStrips(void);
  263.   ~TIFFReadStrips(void);
  264.   void read(IMAGE& image, EndianIn& file);
  265. };
  266.  
  267. TIFFReadStrips::TIFFReadStrips(const TIFFDirectory& directory,
  268.                    const int _rows_per_strip,
  269.                    EndianIn& file)
  270.     : rows_per_strip(_rows_per_strip)
  271. {
  272.   TIFFDirEntry * entryp = directory.look_up(TIFFTAG_STRIPOFFSETS);
  273.   assure( entryp != 0, "STRIPOFFSETS tag must be present in the TIFF file");
  274.  
  275.   no_strips = entryp->count;
  276.   assert( no_strips > 0 );
  277.   strip_offsets = new unsigned long[no_strips];
  278.  
  279.   if( no_strips == 1 )            // The value is coded within the
  280.   {                    // dir entry itself
  281.     if( entryp->type == TIFFDirEntry::SHORT )
  282.       strip_offsets[0] = (entryp->val_offset) >> 16;
  283.     else if( entryp->type == TIFFDirEntry::LONG )
  284.       strip_offsets[0] = entryp->val_offset;
  285.     else
  286.       _error("StripOffsets must contain either LONG or SHORT values");
  287.     return;
  288.   }
  289.  
  290.                     // Otherwise we got to read them,
  291.                     // I mean, the strip offsets
  292.   assert( file.seekg(entryp->val_offset).good() );
  293.   
  294.   register int i;
  295.   for(i=0; i<no_strips; i++)
  296.     if( entryp->type == TIFFDirEntry::SHORT )
  297.       strip_offsets[i] = file.read_short("Reading StripOffsets");
  298.     else if( entryp->type == TIFFDirEntry::LONG )
  299.       strip_offsets[i] = file.read_long("Reading StripOffsets");
  300.     else
  301.       _error("StripOffsets must contain either LONG or SHORT values");
  302. }
  303.  
  304. TIFFReadStrips::~TIFFReadStrips(void)
  305. {
  306.   assert( strip_offsets != 0 );
  307.   delete [] strip_offsets;
  308. }
  309.  
  310.                 // Read in the strips into the image
  311. void TIFFReadStrips::read(IMAGE& image, EndianIn& file)
  312. {
  313.   register int strip;
  314.   for(strip=0; strip<no_strips; strip++)
  315.   {
  316.     assert( file.seekg(strip_offsets[strip]).good() );
  317.  
  318.     register int i,j;            // Reading a strip
  319.     for(i=strip*rows_per_strip; 
  320.     i<(strip+1)*rows_per_strip && i < image.q_nrows(); i++)
  321.       for(j=0; j<image.q_ncols(); j++)
  322.     image(i,j) = file.read_byte("reading a strip");
  323.   }
  324. }
  325.  
  326. /*
  327.  *========================================================================
  328.  *             Root module - actual IMAGE constructor
  329.  */
  330.  
  331. void IMAGE::read_tiff(EndianIn& file, const bool verbose)
  332. {
  333.   message("Reading the TIFF file\n");
  334.  
  335.   TIFFDirectory directory(file);
  336.   
  337.   if( verbose )
  338.     directory.print();
  339.  
  340.   int no_cols = directory.look_up(TIFFTAG_IMAGEWIDTH,0);
  341.   if( no_cols == 0 )
  342.     _error("ImageWidth tag missing or improperly specified as 0");
  343.   
  344.   int no_rows = directory.look_up(TIFFTAG_IMAGELENGTH,0);
  345.   if( no_rows == 0 )
  346.     _error("ImageLength tag missing or improperly specified as 0");
  347.  
  348.                     // Checking if we can handle this
  349.   assert( directory.look_up(TIFFTAG_SAMPLESPERPIXEL,1) == 1 );
  350.   assert( directory.look_up(TIFFTAG_BITSPERSAMPLE,1) == 8 );
  351.   assert( directory.look_up(TIFFTAG_COMPRESSION,COMPRESSION_NONE) == 
  352.      COMPRESSION_NONE );
  353.  
  354.   int photometry = directory.look_up(TIFFTAG_PHOTOMETRIC,
  355.                      PHOTOMETRIC_MINISBLACK);
  356.   assert( photometry == PHOTOMETRIC_MINISBLACK ||
  357.       photometry == PHOTOMETRIC_MINISWHITE );
  358.  
  359.  
  360.   int rows_per_strip = directory.look_up(TIFFTAG_ROWSPERSTRIP,no_rows);
  361.   TIFFReadStrips strips(directory,rows_per_strip,file);
  362.  
  363.   allocate(no_rows,no_cols,8);
  364.  
  365.   {
  366.     char * descr = directory.read_str(TIFFTAG_IMAGEDESCRIPTION,file);
  367.     char * doc_name = directory.read_str(TIFFTAG_DOCUMENTNAME,file);
  368.     if( descr != 0 || doc_name != 0 )
  369.       if( doc_name == 0 )
  370.     name = descr;
  371.       else if( descr == 0 )
  372.     name = doc_name;
  373.       else
  374.     name = strcat(
  375.          strcat(
  376.                    strcpy(new char[strlen(descr)+strlen(doc_name)+2],
  377.               descr),"/"),doc_name),
  378.         delete descr, delete doc_name;
  379.   }
  380.  
  381.  
  382.   strips.read(*this,file);
  383.  
  384.   if( photometry == PHOTOMETRIC_MINISWHITE )
  385.     invert();
  386.  
  387.   cout << "\n" << no_cols << "x" << no_rows << "x" << q_depth() << " image '"
  388.          << name << "' has been read" << endl;
  389. }
  390.  
  391.